package net.cathackers.devel.scmp.chat.backends;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import net.cathackers.devel.scmp.config.impl.XMLDBConfig;
import net.cathackers.devel.scmp.exceptions.BackendConnectException;
import net.cathackers.devel.scmp.exceptions.BackendException;
import net.cathackers.devel.scmp.exceptions.MySQLDriverNotFoundException;
import net.cathackers.devel.scmp.factories.SCMPFactory;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.log4j.Logger;

public class MySQLPoolBackend {
    private boolean isConnected = false;
    protected Logger logger_;
    protected XMLDBConfig conf_;
    private String url_;

    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    private String connection_pool_name ;
    private   String connection_pool_url; 

    public MySQLPoolBackend(XMLDBConfig config, String connectionPoolName, String connection_pool_url) {
	conf_ = config;
	this.connection_pool_name = connectionPoolName;
	this.connection_pool_url = connection_pool_url;
	logger_ = SCMPFactory.getLogger(this);
	url_ = "jdbc:mysql://" + conf_.getHostname() + "/" + conf_.getDBName()
		+ "?user=" + conf_.getUsername() + "&password="
		+ conf_.getPassword();
    }

    public void connect() throws BackendException {
	if (!isConnected()) {
	    try {
		logger_
			.debug("Trying to dynamically load the MySQL JDBC driver.");
		Class.forName(JDBC_DRIVER);
		logger_.debug("MySQL Driver was loaded successfully.");
		logger_.debug("Connecting to database : " + conf_.getDBName()
			+ " on host : " + conf_.getHostname() + " to table : "
			+ conf_.getTableName());
		setupDriver(url_);

	    } catch (ClassNotFoundException ex) {
		logger_.error("Couldn't find the mysql jdbc driver.");
		logger_.error(ex.getMessage());
		throw new MySQLDriverNotFoundException();
	    }
	}

    }

    public void disconnect() throws BackendException {
	shutdownDriver();

    }

    public boolean isConnected() {
	return isConnected;

    }

    public synchronized Connection getConnection() throws BackendConnectException {

	try {
	    return DriverManager.getConnection(connection_pool_url);
	} catch (SQLException ex) {
	    logger_.error("Couldn't establish a connection to database : "
		    + conf_.getDBName() + " at : " + conf_.getHostname()
		    + " to table : " + conf_.getTableName());
	    logger_.error(ex.getMessage());
	    throw new BackendConnectException();
	}
    }

    private void setupDriver(String connectURI) throws BackendException {
	try {
	    ObjectPool connectionPool = new GenericObjectPool(null);
	    ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
		    connectURI, null);
	    // TODO: Do we really need that line?
	    // this Pool will Allow AutoCommit on grabbed connections, if you
	    // want
	    // to disable, false the last arg for constructor.
	    PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(
		    connectionFactory, connectionPool, null, null, false, true);
	    Class.forName("org.apache.commons.dbcp.PoolingDriver");

	    PoolingDriver driver = (PoolingDriver) DriverManager
		    .getDriver("jdbc:apache:commons:dbcp:");
	    driver.registerPool(connection_pool_name, connectionPool);
	    isConnected = true;
	} catch (ClassNotFoundException ex) {
	    logger_
		    .debug("Cannot create object for org.apache.commons.dbcp.PoolingDriver class:"
			    + ex.getMessage());
	    throw new BackendException("Couldn't load pooling driver");
	} catch (SQLException e) {
	    logger_.debug("Cannot load the driver jdbc:apache:commons:dbcp: :"
		    + e.getMessage());
	    throw new BackendException("Couldn't load pooling driver");
	}

    }

    private void shutdownDriver() throws BackendException {
	try {
	    logger_.debug("Shutting down db pool" + connection_pool_name);
	    PoolingDriver driver = (PoolingDriver) DriverManager
		    .getDriver("jdbc:apache:commons:dbcp:");
	    driver.closePool(connection_pool_name);
	    isConnected = false;
	} catch (SQLException e) {
	    logger_.debug("Cannot load the driver jdbc:apache:commons:dbcp: :"
		    + e.getMessage());
	    throw new BackendException("Couldn't load pooling driver");
	}
    
    }
    
    protected void killConnection(Connection conn) throws BackendException {
	try {
	    conn.close();
	} catch (SQLException e) {
	    logger_.error("Couldn't close database connection, possibility of losing connection handlers rise", e);
	    throw new BackendException("Couldn't close the database connection");
	}
    }
    
    protected void printDriverStats() throws BackendException {
	try {

	    PoolingDriver driver = (PoolingDriver) DriverManager
		    .getDriver("jdbc:apache:commons:dbcp:");
	    ObjectPool connectionPool = driver
		    .getConnectionPool(connection_pool_name);
	    logger_.debug("NumActive: " + connectionPool.getNumActive());
	    logger_.debug("NumIdle: " + connectionPool.getNumIdle());
	} catch (SQLException e) {
	    logger_.debug("Cannot load the driver jdbc:apache:commons:dbcp: :"
		    + e.getMessage());
	    throw new BackendException("Couldn't load pooling driver");
	}
    }

}
